﻿#pragma once

#include <learn/dll_ext.h>
#include <learn/geom/vec.h>

namespace xi
{

namespace ais
{

using namespace xi::geom;

class EXT_SYMBOL Camera
{
  protected:
    float m_width, m_height; // 记录宽高
    Vec3f m_center;          // 中心位置

    Vec3f m_front;   // 指向原点的方向
    Vec3f m_up;      // 正交于向前方向的上方
    Vec3f m_right;   // 正交于向前方向的右方
    Vec3f m_worldUp; // 世界上方项，固定不变 (0, 0, 1)

    // 欧拉角：俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll)，我们不需要用到滚转角
    float m_distance; // 到原点的距离
    float m_pitch;    // 与 z 轴的夹角
    float m_yaw;      // 与 x 轴的夹角

    Mat4x4<float> m_viewMat; // 视图矩阵
    Mat4x4<float> m_projMat; // 投影矩阵

  public:
    /**
     * @brief 基本构造函数
     *
     * @param[in] width
     * @param[in] height
     * @param[in] distance
     * @param[in] pitch
     * @param[in] yaw
     */
    Camera(float width, float height, float distance = 10.0f, float pitch = 90.0f, float yaw = 0.0f);

    /**
     * @brief 删除所有赋值和拷贝构造函数
     *
     */
    Camera(Camera &&) = delete;

    /**
     * @brief 视图矩阵
     *
     * @return Mat4x4<float>
     */
    Mat4x4<float> view() const;

    /**
     * @brief 投影矩阵
     *
     * @return Mat4x4<float>
     */
    Mat4x4<float> projection() const;

    /**
     * @brief 获得俯仰角
     *
     * @return float
     */
    float yaw() const;

    /**
     * @brief 获得偏航角
     *
     * @return float
     */
    float pitch() const;

    /**
     * @brief 获得距离
     *
     * @return float
     */
    float distance() const;

    /**
     * @brief 获得相机前方方向
     *
     * @return Vec3f
     */
    Vec3f front() const;

    /**
     * @brief 相机位置
     *
     * @return Vec3f
     */
    Vec3f position() const;

    /**
     * @brief 获得屏幕点射线方向
     *
     * @param[in] pos
     * @return Vec3f
     */
    Vec3f emit(Vec2f pos) const;

    /**
     * @brief 修改屏幕大小
     *
     * @param[in] width
     * @param[in] height
     */
    void resize(float width, float height);

    /**
     * @brief 获得屏幕点对应的空间点
     *
     * @param[in] pos
     * @return Vec2f
     */
    Vec3f space(Vec2f pos) const;

    /**
     * @brief 获得空间点对应的屏幕点
     *
     * @param[in] pos
     * @return Vec3f
     */
    Vec3f screen(Vec3f pos) const;

    /**
     * @brief 设置相机中心
     *
     * @param[in] center
     */
    void set_center(Vec3f center);

    /**
     * @brief 设置俯仰角
     *
     * @param[in] pitch
     */
    void set_pitch(float pitch);

    /**
     * @brief 设置偏航角
     *
     * @param[in] yaw
     */
    void set_yaw(float yaw);

    /**
     * @brief 伸缩相机距离
     *
     * @param[in] ratio
     */
    void scale(float ratio);

    /**
     * @brief 沿着屏幕射线方向平移
     *
     * @param[in] screen
     */
    void translate(Vec2f screen, float d);

    /**
     * @brief 在屏幕上平移
     *
     * @param[in] dv
     */
    void translate(Vec2f dv);

    /**
     * @brief 绕中心点旋转
     *
     * @param[in] dyaw
     * @param[in] dpitch
     */
    void rotate(float dyaw, float dpitch);

    /**
     * @brief 旋转相机方向
     *
     * @param[in] df
     */
    void rotate_front(Vec3f df);

    /**
     * @brief 正视图
     *
     */
    void upright_view();

    /**
     * @brief 右视图
     *
     */
    void right_view();

    /**
     * @brief 俯视图
     *
     */
    void top_view();

    /**
     * @brief 底视图
     *
     */
    void bottom_view();

  protected:
    /**
     * @brief 更新相机方向
     *
     */
    void _update_camera_vectors();
};

} // namespace ais

} // namespace xi
